#-*- encoding:utf-8 -*-

__AUTHOR__ = "newma<newma@live.cn>"

import cv2
import numpy as np

def loadImage(filePath):
	"""
	load image file to numpy data struct.
	:filePath: image file path
	:return: numpy instance with BGR format image data(used in opencv)
	"""
	return cv2.imread(filePath)

def saveImage(filePath, image):
	"""
	save image to filePath	
	:filePath: save path
	:image: numpy instance with BGR format image data(used in opencv)
	:return: If sucess, return True; otherwise, return False
	"""
	try:
		cv2.imwrite(filePath, image)
	except Exception as e:
		print "[Error] saveImage encounter an error: %s" % (str(e))
		return False
	return True

def showImage(image, winName = "default"):
	"""
	show image using opencv GUI tools
	:image:	a numpy instance with BGR format(used in opencv) showImage
	"""
	cv2.imshow(winName, image)

class Billboard(object):
	"""	
		attach on image to anthor image by sepcified polygonal area (it should be limited to having four vertex)
	"""

	def __init__(self, image = None):
		"""
		:image: image who will be attached by other image
		"""
		self.srcImg = image

	def setImage(self, image):
		self.srcImg = image

	def getImage(self):
		return self.srcImg

	def attachImageWithBlur(self, polyVertexs, image, gaussianRange = (55, 55)):
		"""
		:polyVertexs: polygonal points of [(x1, y1), (x2, y2)...]
		:image: image attaches to src image
		:gaussianRange: gaussina kernel range, like (x, y)
		:return: an image numpy instance
		"""
		# Construct image shape points
		size = image.shape
		imgVertexs = np.array(
                       [
                        [0,0],
                        [size[1] - 1, 0],
                        [size[1] - 1, size[0] -1],
                        [0, size[0] - 1 ]
                        ],dtype=float
                       )
		if type(polyVertexs) is list:
			polyVertexs = np.vstack(polyVertexs).astype(float)
		# Calculate Homography 
		homo, status = cv2.findHomography(imgVertexs, polyVertexs)
		# Warp source image
		warpImg = cv2.warpPerspective(image, homo, (self.srcImg.shape[1], self.srcImg.shape[0]))
		
		# Polygonal area mask in src image.
		imMask = np.zeros((self.srcImg.shape[0], self.srcImg.shape[1]), dtype = np.uint8)
		cv2.fillConvexPoly(imMask, polyVertexs.astype(int), 255, cv2.LINE_4)
		
		# Blur layer
		imBlur = np.empty_like(self.srcImg)
		imBlur[:] = self.srcImg
		imBlur = cv2.GaussianBlur(imBlur, gaussianRange, 25)
		# Merge Blur layers
		self.srcImg = self.mergeTwoLayer(imBlur, self.srcImg, mask = imMask)

		# Merge warp image layer
		imgGray = cv2.cvtColor(warpImg, cv2.COLOR_BGR2GRAY)
		res, warpMask = cv2.threshold(imgGray, 1, 255, cv2.THRESH_BINARY)
		if not res :
			print "[Warning]: cv2.threadhold can't find mask of attaching image, using image size as mask"
			warpMask = imMask
		self.srcImg = self.mergeTwoLayer(warpImg, self.srcImg, mask = warpMask)

		return self.srcImg

	def mergeTwoLayer(self, imageForce, imageBackground, mask):
		imageUp = cv2.bitwise_and(imageForce, imageForce, mask = mask)
		invMask = cv2.bitwise_not(mask)
		imgDown = cv2.bitwise_and(imageBackground, imageBackground, mask = invMask)
		return imageUp + imgDown

	def getCompressImage(self, reduceSize):
		"""
		reduce image size by special size
		:reduceSize: reduce size, it must be any power of 2. The big
		:return: compressed file
		"""
		img = self.srcImg
		count = reduceSize >> 1
		for i in xrange(count, 0, -1):
			img = cv2.pyrDown(img)

		return img

if __name__ == '__main__':
	car = loadImage('car.png')
	window = loadImage('3.jpeg')
	pts = [(335, 134), (494, 134), (489, 311), (336, 342)]
	bb = Billboard(window)
	img = bb.attachImageWithBlur(pts, car, gaussianRange = (25, 25))
	saveImage('attached.jpeg', img)
	img = bb.getCompressImage(4)
	saveImage('compressed.jpeg', img)
	print 'Done!'
